1 /** 2 Copyright: Copyright (c) 2018, Joakim Brännström. All rights reserved. 3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This module contains the registry of analaysers 7 */ 8 module code_checker.engine.registry; 9 10 import logger = std.experimental.logger; 11 import std.exception : collectException; 12 13 import code_checker.engine.types; 14 15 @safe: 16 17 /// The type of an analyser which then affect the order they are executed. 18 enum Type { 19 staticCode, 20 dynamic, 21 } 22 23 struct Registry { 24 private { 25 BaseFixture[][Type] analysers; 26 } 27 28 void put(BaseFixture a, Type t) { 29 assert(a !is null); 30 31 if (auto v = t in analysers) { 32 (*v) ~= a; 33 } else { 34 analysers[t] = [a]; 35 } 36 } 37 38 /// Range over the analysers. ! 39 auto range() { 40 import std.array : array; 41 import std.algorithm : map, joiner, filter; 42 43 const order = [Type.staticCode, Type.dynamic]; 44 45 auto getAnalysers(Type t) { 46 if (auto v = t in analysers) 47 return (*v).map!(a => AnalyserRange.Pair(t, a)).array; 48 return null; 49 } 50 51 return order.map!(a => getAnalysers(a)).filter!(a => a !is null).joiner.array; 52 } 53 } 54 55 /// Returns: The total status of running the analyzers. 56 Status execute(Environment env, ref Registry reg) @trusted { 57 import std.algorithm; 58 import std.range; 59 60 TotalResult tres; 61 62 void handleResult(Result res_) nothrow { 63 // we know the thread finished and have the only copy. 64 // immutable is a bit cumbersome for now so throw away it to keep the 65 // code somewhat efficient. 66 auto res = cast() res_; 67 68 try { 69 log(res.msg); 70 71 tres.status = mergeStatus(tres.status, res.status); 72 tres.score = Score(tres.score + res.score); 73 tres.sugg ~= res.msg.array.filter!(a => a.severity == MsgSeverity.improveSuggestion) 74 .array; 75 76 logger.trace(res); 77 logger.trace(tres); 78 } catch (Exception e) { 79 logger.warning("Failed executing all tests").collectException; 80 logger.warning(e.msg).collectException; 81 tres.status = Status.failed; 82 } 83 } 84 85 foreach (a; reg.range) { 86 logger.infof("%s: %s", a.type, a.analyzer.explain); 87 a.analyzer.putEnv(env); 88 handleResult(executeOneAnalyzer(a.analyzer)); 89 } 90 91 log(tres); 92 return tres.status; 93 } 94 95 Result executeOneAnalyzer(BaseFixture a) nothrow @trusted { 96 Result r; 97 try { 98 a.setup; 99 a.execute; 100 a.tearDown; 101 r = a.result; 102 } catch (Exception e) { 103 logger.error(e.msg).collectException; 104 r.status = Status.failed; 105 } 106 107 return r; 108 } 109 110 private: 111 112 void log(Messages msgs) { 113 import std.algorithm : sort; 114 115 foreach (m; msgs.value.sort) { 116 final switch (m.severity) { 117 case MsgSeverity.improveSuggestion: 118 break; 119 case MsgSeverity.unableToExecute: 120 case MsgSeverity.failReason: 121 logger.warning(m.value); 122 break; 123 } 124 } 125 } 126 127 void log(TotalResult tres) { 128 import std.conv : to; 129 import colorize; 130 131 logger.infof("Executing analysers %s", tres.status == Status.failed 132 ? "Failed".color(Color.red) : "Passed".color(Color.green)); 133 134 if (tres.sugg.length > 0) { 135 logger.info("Suggestions for how to improve the score"); 136 foreach (m; tres.sugg) 137 logger.info(" ", m.value); 138 } 139 140 if (tres.status == Status.passed) { 141 logger.info("Congratulations!!!"); 142 logger.infof("Your code reached Quality Level %s", 1 + tres.score / 10); 143 } 144 145 const string score = () { 146 if (tres.score < 0) 147 return tres.score.to!string.color(Color.red, Background.init, Mode.bold); 148 return tres.score.to!string; 149 }(); 150 logger.infof("You scored %s points", score); 151 152 if (tres.score < 0) { 153 logger.info("Sorry, your code needs a major rework to reach an acceptable quality level"); 154 } 155 } 156 157 /// Input range over the analysers. 158 struct AnalyserRange { 159 import std.typecons : Tuple; 160 161 alias Pair = Tuple!(Type, "type", BaseFixture, "analyzer"); 162 163 Pair[] r; 164 165 auto front() @safe pure nothrow { 166 assert(!empty, "Can't get front of an empty range"); 167 return r[0]; 168 } 169 170 void popFront() @safe pure nothrow { 171 assert(!empty, "Can't pop front of an empty range"); 172 r = r[1 .. $]; 173 } 174 175 bool empty() @safe pure nothrow const @nogc { 176 return r.length == 0; 177 } 178 }